﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using System.Data;

namespace MyClever.Lib
{
    class CSharpPluginBuilder : PluginBuilder
    {
        private Assembly pluginAssembly;

        public CSharpPluginBuilder(PluginPath pluginPath, string pluginClass, ProgrammingLanguage programmingLanguage)
            : base(pluginPath, pluginClass, programmingLanguage)
        {
            this.pluginAssembly = Assembly.Load(this.path.Name);
            // TODO: hat es das verlangte Interface?

            this.properties.Add(PluginProperty.Language, ProgrammingLanguage.CSharp);
        }
        
        public override void ExtractProperties()
        {            
            foreach (Type assemblyTypeOn in this.pluginAssembly.GetTypes())
            {
                // ... for our plugin 
                if (true == assemblyTypeOn.IsClass && true == assemblyTypeOn.IsPublic && assemblyTypeOn.FullName.Equals(this.className, StringComparison.CurrentCultureIgnoreCase))
                {
                    // ... to get his properties
                    object[] packageAttributes = assemblyTypeOn.GetConstructor(new Type[0]).GetCustomAttributes(typeof(PluginAttribute), false);
                    if (packageAttributes.Length > 0)
                    {
                        PluginAttribute attribute = (PluginAttribute)packageAttributes[0];
                        foreach (KeyValuePair<PluginProperty, object> attributeOn in attribute.attributes)
                        {
                            this.properties.Add(attributeOn.Key, attributeOn.Value);
                        }
                    }
                }
            }
        }

        public override void ExtractPackages()
        {
            // get all package from the given direction
            foreach (KeyValuePair<Type, PackageAttribute> packageAttributeOn in this.GetPackageTypesByAssemblyCode())
            {
                // get all columns-attributes of this package
                List<KeyValuePair<string, ColumnAttribute>> columnAttributes = this.GetColumnAttributes(packageAttributeOn.Key);
                PackageDefinition packageDefinition = new PackageDefinition(packageAttributeOn.Key.Name, packageAttributeOn.Value.Direction, packageAttributeOn.Value.Description, columnAttributes.Count == 0);

                // run through every column-attribue ...
                foreach (KeyValuePair<string, ColumnAttribute> columnAttributeOn in columnAttributes)
                {
                    // ...to create a new column
                    PackageColumnDefinition column = new PackageColumnDefinition(columnAttributeOn.Key, columnAttributeOn.Value.ValueType);

                    // save the description of the column
                    column.extendedProperties.Add(MissionPluginPackageColumn.ExtendedPropertiesKey.Description, columnAttributeOn.Value.Description);

                    // is there any validation-rule? (optional!)
                    if (false == String.IsNullOrEmpty(columnAttributeOn.Value.Validation))
                    {
                        column.extendedProperties.Add(MissionPluginPackageColumn.ExtendedPropertiesKey.Validation, columnAttributeOn.Value.Validation);
                    }

                    // is there any default-value? (optional!)
                    if (false == String.IsNullOrEmpty(columnAttributeOn.Value.DefaultValue))
                    {
                        column.defaultValue = columnAttributeOn.Value.DefaultValue;
                    }
                    packageDefinition.columnDefinitions.Add(column);
                }
                this.packages.Add(packageDefinition);
            }
        }

        public override object GetInstance()
        {
            Debug.Assert(this.pluginAssembly != null);
            return this.pluginAssembly.CreateInstance(this.className);            
        }

        private List<KeyValuePair<Type, PackageAttribute>> GetPackageTypesByAssemblyCode()
        {
            List<KeyValuePair<Type, PackageAttribute>> packages = new List<KeyValuePair<Type, PackageAttribute>>();

            foreach (Type assemblyTypeOn in this.pluginAssembly.GetTypes())
            {
                // ...for all package-types 
                if (true == assemblyTypeOn.IsEnum)
                {
                    // ... defined in the expected plugin
                    if (null != assemblyTypeOn.DeclaringType && assemblyTypeOn.DeclaringType.FullName.Equals(this.className, StringComparison.CurrentCultureIgnoreCase))
                    {
                        // ... with a packageAttribute
                        object[] packageAttributes = assemblyTypeOn.GetCustomAttributes(typeof(PackageAttribute), false);
                        if (packageAttributes.Length > 0)
                        {
                            PackageAttribute attribute = (PackageAttribute)packageAttributes[0];

                            KeyValuePair<Type, PackageAttribute> packageType = new KeyValuePair<Type, PackageAttribute>(assemblyTypeOn, attribute);
                            packages.Add(packageType);
                        }
                    }
                }
            }
            return packages;
        }

        private List<KeyValuePair<string, ColumnAttribute>> GetColumnAttributes(Type packageEnum)
        {
            List<KeyValuePair<string, ColumnAttribute>> columnAttributes = new List<KeyValuePair<string, ColumnAttribute>>();
            foreach (FieldInfo enumField in packageEnum.GetFields())
            {
                // is really an enum-field? (no type-information)
                if (false == enumField.IsSpecialName)
                {
                    foreach (ColumnAttribute columnAttribute in enumField.GetCustomAttributes(typeof(ColumnAttribute), false))
                    {
                        columnAttributes.Add(new KeyValuePair<string, ColumnAttribute>(enumField.Name, columnAttribute));
                    }
                }
            }
            return columnAttributes;
        }
    }
}
